package beans.factorys.support;

import beans.BeanDefinition;
import beans.factorys.BeanCreationException;
import beans.factorys.BeanDefinitionRegistry;
import beans.factorys.BeanFactory;
import beans.factorys.config.ConfigurableBeanFactory;
import utils.Assert;
import utils.ClassUtils;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Created by ZhaiGuaili on 2018/6/29.
 */
public class DefaultBeanFactory extends DefaultSingletonBeanRegistry implements ConfigurableBeanFactory, BeanDefinitionRegistry {

    private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>(10);

    private ClassLoader classLoader;

    @Override
    public void RegistryBeanDefinition(String beanId, BeanDefinition bd) {
        this.beanDefinitionMap.put(beanId,bd);
    }

    @Override
    public BeanDefinition getBeanDefinition(String beanId) {

        return this.beanDefinitionMap.get(beanId);
    }

    @Override
    public Object getBean(String beanId) throws Exception {

        BeanDefinition bd = this.beanDefinitionMap.get(beanId);
        if (bd == null) {
            throw new BeanCreationException("beanDefinition is not exist");
        }
        if(bd.isSingleton()){
            Object singletonBean = this.getSingleton(beanId);
            if(singletonBean == null){
                singletonBean = createBean(bd);
                this.registrySingleton(beanId,singletonBean);
            }
            return singletonBean;
        }
        return createBean(bd);
    }



    private Object createBean(BeanDefinition bd) throws Exception{
        Assert.notNull(bd,"'beanDefinition' must not be null");
        ClassLoader cl = this.getBeanClassLoader();
        String beanClassName = bd.getBeanClassName();
        try {
            Class<?> clazz = cl.loadClass(beanClassName);
            return clazz.newInstance();
        }catch (BeanCreationException e){
            throw new BeanCreationException(beanClassName,"create bean for "+ beanClassName +" failed",e);
        }
    }



    @Override
    public void setBeanClassLoader(ClassLoader beanClassLoader) {
        this.classLoader = beanClassLoader;
    }

    @Override
    public ClassLoader getBeanClassLoader() {
        return (this.classLoader != null?this.classLoader:ClassUtils.getDefaultClassLoader());
    }
}
